package model.FTRS;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.lang.Math;


import model.CompressionManager;
import model.archivo.ArchivoBloques;
import model.archivo.ArchivoDiccionario;
import model.archivo.ArchivoRegistrosVariables;
import model.bloque.Bloque;
import model.extras.CaseFolding;
import model.extras.ParserLinea;
import model.extras.SortExterno;
import model.extras.StopWords;
import model.registro.RegistroAudio;
import model.registro.RegistroDiccionario;
import model.registro.RegistroDocumentos;
import model.registro.RegistroDocumentosDisponibles;
import model.registro.RegistroFTRS;
import model.registro.RegistroIndicePesoGlobal;
import model.registro.RegistroOrdAparicion;


public class Index {

	private ParserLinea parser = new ParserLinea(" "+","+"."+";"+":"+"-"+"("+")"+"\""+"{"+"}"+"'"+"["+"]"+"\n"+"\n\r");
	private int tamBloque;
	private Bloque bloqueActual;
	private ArchivoDiccionario archLexico;
	private ArchivoDiccionario archOrdAparicion;
	private ArchivoBloques archAux;
	private ArchivoDiccionario archIndicePeso;
	private ArchivoDiccionario archSortOrdAparicion;
	
	
	public Index(ArchivoBloques archAux) {
		super();
		this.archAux = archAux;
		this.tamBloque = 512;
		this.bloqueActual = new Bloque(0, tamBloque);
		try {
			this.archLexico =  new ArchivoDiccionario("sistema/SortLexico.dat", true);
			this.archOrdAparicion= new ArchivoDiccionario("sistema/SortOrdAparicion.dat", true);
		} catch (Exception e) {
			System.out.println("Error con archivo");
		}
	}
	
	public int LoadDocumentsFromDisc(ArchivoRegistrosVariables archivoDocumentos,ArrayList<RegistroDocumentosDisponibles> DocumentosDisponibles) throws Exception
	{
		ArrayList<RegistroOrdAparicion> ordAparicionList = new ArrayList<RegistroOrdAparicion>();
		ArrayList<String> lexico = new ArrayList<String>();
		int cantDocs = 0;
		RegistroIndicePesoGlobal indicePeso; 
		ArrayList<RegistroIndicePesoGlobal> listPeso = new ArrayList<RegistroIndicePesoGlobal>();
		String fullText;
		archivoDocumentos.rwArchivo();
		this.archLexico.rwArchivo();
		this.archOrdAparicion.rwArchivo();
		int offset = 0;
		int algoritmo = 0;
		
		while (offset != archivoDocumentos.getOffSetEOF()) {
			algoritmo=DocumentosDisponibles.get(cantDocs).getAlgoritmoCompresion();
			cantDocs++;
			
			RegistroAudio registroTexto = new RegistroAudio();
			archivoDocumentos.leerRegistro(offset, registroTexto);
			
			//for (int i=0; i<DocumentosDisponibles.size();i++)
			//	if (DocumentosDisponibles.get(i).getOffSet()==offset)
			//		algoritmo=DocumentosDisponibles.get(i).getAlgoritmoCompresion();			
			
			byte[] bytesTextoSinComprimir = null;
			
			switch (algoritmo) { 
			case 1:
				bytesTextoSinComprimir=CompressionManager.DescomprimirAritmetico(registroTexto.convertirRegistroABytes());
				break;
			case 2:
				bytesTextoSinComprimir=CompressionManager.DescomprimirLZP(registroTexto.convertirRegistroABytes());
				break;
			case 3:
				bytesTextoSinComprimir=CompressionManager.DescomprimirPPMC(registroTexto.convertirRegistroABytes());
				break;
			default:
				bytesTextoSinComprimir=registroTexto.convertirRegistroABytes();
				break;
			}
			
			fullText = new String(bytesTextoSinComprimir, 0, bytesTextoSinComprimir.length, "UTF-16BE");
			fullText = CaseFolding.ApplyCaseFolding(fullText);

			
			ArrayList<String> words = new ArrayList<String>();

			for (String word : parser.LineToArray(fullText)) {
				words.add(word);
			}			
						
			//Se remueven todas las palabras que no se indexaran
			StopWords stopWords = new StopWords();
			ArrayList<String> noWords = stopWords.getNoGuardar();
			for (String noWord : noWords) {
				while (words.contains(noWord))
					words.remove(noWord);
			}			

		
			//words contiene todas las palabras del doc sin los stopwords
			// y en lexico se van a guardar las palabras por orden de aparicion
			float frecLocalTermino;
			for (String word : words) {
				//Se remueven los duplicados
				
				if (!lexico.contains(word)){
					lexico.add(word);
					RegistroDiccionario reg = new RegistroDiccionario(word,word.length());
					archLexico.escribirRegistro(reg);
					indicePeso = new RegistroIndicePesoGlobal(lexico.indexOf(word),1);
					listPeso.add(indicePeso);
					
				}
				
				boolean existe = false;
				boolean aumento = false;
				
				RegistroOrdAparicion reg = new RegistroOrdAparicion(lexico.indexOf(word),offset,words.size(),1,1);
								
				//si la lista de oreden de aparicion esta vacia, se agrega el registro
				if (ordAparicionList.isEmpty()){
					ordAparicionList.add(reg);
					
				}
				else {
				   for (RegistroOrdAparicion reg2 : ordAparicionList) {
					   //Si la palabra ya existe en ese documento, se aumenta su cantidad dentro del documento
					   //Se sale del ciclo porque sabemos que solo existe una vez
					   if ((reg2.getNroAparicion() == reg.getNroAparicion()) && (reg2.getOffsetDoc() == reg.getOffsetDoc())){
						        frecLocalTermino= reg2.getRepeticiones();
						        reg2.setRepeticiones(frecLocalTermino+1);  
						        existe = true;
						        
				       }
					   if ((reg2.getNroAparicion() == reg.getNroAparicion()) && (reg2.getOffsetDoc() != reg.getOffsetDoc())){
					          aumento = true;
					    }
				   }   
				   if (!existe) {
					   ordAparicionList.add(reg);
				   }	
				   if ((!existe)&&(aumento)){
					   aumentarPeso(listPeso,reg.getNroAparicion());
					   
				   }
					   
				}
			}
			
			offset = (int)archivoDocumentos.getFilePointer();
			
		}
		if (cantDocs==0) {
			System.out.println("INFORMACION: No se han cargado documentos.\n");
			return -1;
		}
		else {
			for (RegistroOrdAparicion reg : ordAparicionList) {
				//System.out.println("se guarda en disco "+reg.getNroAparicion());
				    archOrdAparicion.escribirRegistro(reg);
			}
			
			this.archLexico.cerrarArchivo();
			this.archOrdAparicion.cerrarArchivo();
			CompactAndCalculateFrecuency(listPeso,cantDocs);
			//System.out.println("INFORMACION: Se han indexado "+cantDocs+" documentos.\n");
			return 0;
		}
	}
	
	private void CompactAndCalculateFrecuency(ArrayList<RegistroIndicePesoGlobal> listaPeso, int cantDocs) throws IOException
	{
		this.archIndicePeso =  new ArchivoDiccionario("sistema/SortIndicePeso.dat", true);
		this.archOrdAparicion= new ArchivoDiccionario("sistema/SortOrdAparicion.dat", false);
		ArrayList<RegistroOrdAparicion> ordAparicionList = new ArrayList<RegistroOrdAparicion>();
				
		//System.out.println("Cantidad de documentos en la colccion: " +cantDocs);
		
		
		for (RegistroIndicePesoGlobal reg : listaPeso){
			
			Float auxF = new Float((float)cantDocs/reg.getPesoGlobal());
			Double auxD = auxF.doubleValue();
			Double pesoD= Math.log10(auxD.doubleValue());
			float pesoP = pesoD.floatValue();
			
			reg.setPesoGlobal(pesoP);
		}
		
		this.archIndicePeso.rwArchivo();
		
		for (RegistroIndicePesoGlobal reg : listaPeso) {
			    archIndicePeso.escribirRegistro(reg);
		}
		
		boolean exists = false;
		
		this.archSortOrdAparicion= new ArchivoDiccionario("sistema/SortExternoOrdAparicion.dat", true);
		SortExterno.mergeSort(this.archOrdAparicion, this.archIndicePeso, this.archSortOrdAparicion);
		
		//this.archOrdAparicion= new ArchivoDiccionario("sistema/SortOrdAparicion.dat", true);
		this.archSortOrdAparicion.rwArchivo();
		
        int offset = 0;
		
        this.archOrdAparicion.rwArchivo();
		while (offset != archSortOrdAparicion.getOffSetEOF()) {
			RegistroOrdAparicion regOrdAparicion = new RegistroOrdAparicion();
		    archSortOrdAparicion.leerRegistro(regOrdAparicion);
		   
		    offset =  (int)archSortOrdAparicion.getFilePointer();
		    exists = false;
		    				
			if (!ordAparicionList.isEmpty())
			for (RegistroOrdAparicion reg : ordAparicionList) {
				if ((reg.getNroAparicion() == regOrdAparicion.getNroAparicion()) &&(reg.getOffsetDoc() == regOrdAparicion.getOffsetDoc())){
					exists = true;
					break;
				}
			}
			if (!exists) {
				ordAparicionList.add(regOrdAparicion);
			}
		}
		this.archOrdAparicion= new ArchivoDiccionario("sistema/SortOrdAparicion.dat", true);
		this.archOrdAparicion.rwArchivo();
		
		for (RegistroOrdAparicion reg : ordAparicionList) {
		    archOrdAparicion.escribirRegistro(reg);
	     }
		
		this.archIndicePeso.cerrarArchivo();
		this.archOrdAparicion.cerrarArchivo();
		this.archSortOrdAparicion.cerrarArchivo();
		//this.archLexico.cerrarArchivo();
	}
	
	public ArrayList<RegistroFTRS> CreateDocsList() throws FileNotFoundException, IOException
	{
		int i=0;
		
		
		ArrayList<String> lexicoList = new ArrayList<String>();
		ArrayList<RegistroOrdAparicion> ordAparicionList = new ArrayList<RegistroOrdAparicion>();
		ArrayList<RegistroFTRS> listaFTRS= new ArrayList<RegistroFTRS>();
		
		
        //Este registro solo contiene una lista de enteros para guardar los offsets 
		RegistroDocumentos regDocs;
		//Este registro contiene la palabra, el puntero a la lista de docs y el peso global de la palabra
		RegistroFTRS regFtrs;
		
		
		this.archLexico =  new ArchivoDiccionario("sistema/SortLexico.dat", false);
		this.archOrdAparicion= new ArchivoDiccionario("sistema/SortOrdAparicion.dat", false);
		
		this.archOrdAparicion.rwArchivo();
		this.archLexico.rwArchivo();
		
		int offset = 0;		
		
		while (offset != this.archOrdAparicion.getOffSetEOF()) {
			RegistroOrdAparicion regOrdAparicion = new RegistroOrdAparicion();      
			this.archOrdAparicion.leerRegistro(regOrdAparicion);
		    ordAparicionList.add(regOrdAparicion);
		    offset =  (int)this.archOrdAparicion.getFilePointer();		
		}
		
		
        offset = 0;	
        
		while (offset != archLexico.getOffSetEOF()) {
			RegistroDiccionario regDic = new RegistroDiccionario();
		    archLexico.leerRegistro(regDic);
		    lexicoList.add(regDic.getPalabra());
		    offset =  (int)this.archLexico.getFilePointer();		
		}
		
		
		float pesoGlobal=0;
		int idBloque = -1;
		for (String word : lexicoList) {
			
		    ArrayList<Integer> array = new ArrayList<Integer>();
			
			while ((i<ordAparicionList.size()) && (ordAparicionList.get(i).getNroAparicion() == lexicoList.indexOf(word))) {
				//agrego a la lista todos los documentos que pertenecen a una palabra
				array.add(ordAparicionList.get(i).getOffsetDoc());		        
				pesoGlobal = ordAparicionList.get(i).getPesoGlobal();
				i++;
			}
			
			regDocs = new RegistroDocumentos(word,array);
			if (this.bloqueActual.agregarRegistroABloque(regDocs.convertirRegistroABytes()) >= 0) {
				idBloque = bloqueActual.getIdBloque();
			}else {
				this.archAux.EscribirBloque(bloqueActual.convertirBloqueABytes(), bloqueActual.getIdBloque());
				this.bloqueActual = new Bloque((int)this.archAux.GetCantidadBloques(),tamBloque);
				if (this.bloqueActual.agregarRegistroABloque(regDocs.convertirRegistroABytes()) >= 0) {
					idBloque = bloqueActual.getIdBloque();
				}
				else {
					System.out.println("ERROR");
				}
			} 
			
			//el primer offset a la lista de docs es el 0 
			regFtrs = new RegistroFTRS(word,idBloque,pesoGlobal);
			listaFTRS.add(regFtrs);
			         			
		}
		this.archAux.EscribirBloque(this.bloqueActual.convertirBloqueABytes(), this.bloqueActual.getIdBloque());		
		
		return listaFTRS;
			
	}
	
	private void aumentarPeso(ArrayList<RegistroIndicePesoGlobal> lista, int pos){
		int peso;
		for (RegistroIndicePesoGlobal reg: lista){
			if (reg.getNroAparicion()==pos){
				peso = (int) reg.getPesoGlobal()+ 1;
				reg.setPesoGlobal(peso);
			}
		}
	}

}
